/*
 * NMap.h
 *
 *  Created on: Aug 14, 2014
 *      Author: burek
 */

#ifndef NMAP_H_
#define NMAP_H_

#include "integers.h"
#include "../util.h"
#include "../common.h"
#include "../log/Log.h"

#include <cstdlib>
#include <cstring>
#include <assert.h>

#define NMAP_HASH_LIST_SIZE (256)
#define NMAP_MAX_KEY_SIZE (128)

template <typename T>
struct NMap_Node
{
	SMap_Node * next;
	uint32_t key;
	const T * value;
	bool inUse;
};

template <typename T>
class NMap
{
private:

	NMap_Node<T> * startPoints[NMAP_HASH_LIST_SIZE];
	int inUseCount;
	int count;

	bool deleteOnExit;

	NMap_Node<T> * m_get(uint32_t key, bool create)
	{
		uint32_t signature = key & (SMAP_HASH_LIST_SIZE - 1);

		NMap_Node<T> * node = startPoints[signature];

		if(node == NULL)
		{
			if(create)
			{
				node = startPoints[signature] = new NMap_Node<T>();
				node->key = key;
				node->next = NULL;
				node->inUse = true;
				count++;
				inUseCount++;
			}
			return node;
		}
		else
		{
			NMap_Node<T> * firstEmpty = NULL;

			while(node != NULL)
			{
				if(firstEmpty == NULL && !node->inUse)
					firstEmpty = node;

				if(node->inUse && node->key == key)
					return node;

				if(node->next == NULL && create)
				{
					if(firstEmpty != NULL)
					{
						firstEmpty->key = key;
						firstEmpty->inUse = true;
						inUseCount++;
						return firstEmpty;
					}
					else
					{
						node->next = new NMap_Node<T>();
						node->next->key = key;
						node->next->next = NULL;
						node->next->inUse = true;
						count++;
						inUseCount++;
						return node->next;
					}
				}

				node = node->next;
			}

			return NULL;
		}
	}

	void m_put(uint32_t key, const T * value)
	{
		NMap_Node<T> * node = m_get(key, true);
		assert(node != NULL);
		node->inUse = true;
		node->value = value;
	}

public:
	NMap()
	{
		count = 0;
		inUseCount = 0;
		deleteOnExit = false;
		memset(startPoints, 0, NMAP_HASH_LIST_SIZE * sizeof(void*));
	}

	~NMap()
	{
		clear(deleteOnExit);
	}

	void clear(bool doDelete = false)
	{
		for(int i = 0; i < NMAP_HASH_LIST_SIZE; ++i)
		{
			if(startPoints[i] != NULL)
			{
				NMap_Node<T> * next = startPoints[i];
				while(next != NULL)
				{
					NMap_Node<T> * prev = next;
					if(doDelete && prev->inUse)
						delete prev->value;
					next = next->next;
					delete prev;
				}
				startPoints[i] = NULL;
			}
		}

		count = 0;
		inUseCount = 0;
	}

	void setDeleteOnExit(bool doe)
	{
		deleteOnExit = doe;
	}

	void put(uint32_t key, const T * value)
	{
		m_put(key, value);
	}

	T * get(uint32_t key, const T * def = NULL)
	{
		NMap_Node<T> * node = m_get(key, false);
		if(node == NULL)
			return const_cast<T*>(def);
		else
			return const_cast<T*>(node->value);
	}

	void remove(uint32_t key, bool doDelete = false)
	{
		NMap_Node<T> * node = m_get(key, false);
		if(node != NULL)
		{
			node->inUse = false;
			if(doDelete)
				delete node->value;
			inUseCount--;
			assert(inUseCount >= 0);
		}
	}

	int getCount() const
	{
		return inUseCount;
	}

	T * getNthValue(int N)
	{
		if(N < 0 || N >= inUseCount)
			return NULL;

		for(int i = 0; i < NMAP_HASH_LIST_SIZE; ++i)
		{
			NMap_Node<T> * n = startPoints[i];
			while(n != NULL)
			{
				if(n->inUse && N <= 0)
					return const_cast<T*>(n->value);

				if(n->inUse)
					N--;

				n = n->next;
			}
		}

		return NULL;
	}

	uint32_t * getNthKey(int N)
	{
		if(N < 0 || N >= inUseCount)
			return NULL;

		for(int i = 0; i < NMAP_HASH_LIST_SIZE; ++i)
		{
			NMap_Node<T> * n = startPoints[i];
			while(n != NULL)
			{
				if(n->inUse && N <= 0)
					return n->key;

				if(n->inUse)
					N--;

				n = n->next;
			}
		}

		return NULL;
	}

	void dumpInfo()
	{
		RLog("NMap count = %d / %d", inUseCount, count);
		for(int i = 0; i < SMAP_HASH_LIST_SIZE; ++i)
		{
			if(startPoints[i] != NULL)
			{
				int nodeCount = 0;
				int inUse = 0;

				NMap_Node<T> * n = startPoints[i];
				while(n != NULL)
				{
					nodeCount++;
					if(n->inUse) inUse++;
					n = n->next;
				}

				RLog("NODE [%d] = %d / %d", i, inUse, nodeCount);
			}
		}
		RLog("---");
	}
};

#endif /* NMAP_H_ */
